﻿using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;

namespace IdentityManagement
{
    public class IdentityDataSeeder : ITransientDependency, IIdentityDataSeeder
    {
        protected IGuidGenerator GuidGenerator { get; }
        protected IIdentityRoleRepository RoleRepository { get; }
        protected IIdentityUserRepository UserRepository { get; }
        protected IOrganizationUnitRepository OrganizationUnitRepository { get; }
        protected ILookupNormalizer LookupNormalizer { get; }
        protected IdentityUserManager UserManager { get; }
        protected IdentityRoleManager RoleManager { get; }
        protected OrganizationUnitManager OrganizationUnitManager { get; }
        protected ICurrentTenant CurrentTenant { get; }
        protected IOptions<IdentityOptions> IdentityOptions { get; }

        public IdentityDataSeeder(
            IGuidGenerator guidGenerator,
            IIdentityRoleRepository roleRepository,
            IIdentityUserRepository userRepository,
            IOrganizationUnitRepository organizationUnitRepository,
            ILookupNormalizer lookupNormalizer,
            IdentityUserManager userManager,
            IdentityRoleManager roleManager,
            OrganizationUnitManager organizationUnitManager,
            ICurrentTenant currentTenant,
            IOptions<IdentityOptions> identityOptions)
        {
            GuidGenerator = guidGenerator;
            RoleRepository = roleRepository;
            UserRepository = userRepository;
            OrganizationUnitRepository = organizationUnitRepository;
            LookupNormalizer = lookupNormalizer;
            UserManager = userManager;
            RoleManager = roleManager;
            OrganizationUnitManager = organizationUnitManager;
            CurrentTenant = currentTenant;
            IdentityOptions = identityOptions;
        }

        [UnitOfWork]
        public virtual async Task SeedAsync(
            string adminPhoneNumber,
            string adminEmail,
            string adminPassword,
            Guid? tenantId,
            string tenantName,
            string tenantDisplayName,
            ExtraPropertyDictionary rootOUExtraProperties,
            ExtraPropertyDictionary adminExtraProperties,
            ExtraPropertyDictionary adminRoleExtraProperties)
        {
            Check.NotNullOrWhiteSpace(adminPassword, nameof(adminPassword));

            using (CurrentTenant.Change(tenantId))
            {
                await IdentityOptions.SetAsync();

                var tid = tenantId ?? Guid.Empty;
                var organization = await (await OrganizationUnitRepository.AsNoTrackingAsync()).Where(r => r.Id == tid).FirstOrDefaultAsync();

                if (organization == null)
                {
                    organization = new OrganizationUnit(tenantId ?? GuidGenerator.Create(), tenantName, tenantDisplayName, null, tenantId, rootOUExtraProperties)
                    {
                        Type = 1,
                        Code = "00001"
                    };
                    await OrganizationUnitManager.CreateAsync(organization);
                }

                //"admin" user
                const string adminUserName = "admin";
                var adminUser = await UserRepository.FindByNormalizedUserNameAsync(
                    LookupNormalizer.NormalizeName(adminUserName)
                );

                if (adminUser == null)
                {
                    adminUser = new IdentityUser(
                        GuidGenerator.Create(),
                        adminUserName,
                        adminPhoneNumber,
                        adminEmail,
                        tenantId,
                        adminExtraProperties
                    )
                    {
                        Name = adminUserName
                    };

                    (await UserManager.CreateAsync(adminUser, adminPassword, validatePassword: false))
                        .CheckErrors();
                }


                //"admin" role
                const string adminRoleName = "管理员";
                var adminRole =
                    await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName));
                if (adminRole == null)
                {
                    adminRole = new IdentityRole(
                        tenantId ?? GuidGenerator.Create(),
                        adminRoleName,
                        tenantId,
                        adminRoleExtraProperties
                    )
                    {
                        IsStatic = true,
                        IsPublic = true,
                        OrganizationUnitId = organization.Id,
                    };

                    (await RoleManager.CreateAsync(adminRole)).CheckErrors();
                }

                if (!adminUser.IsInRole(adminRole.Id))
                {
                    (await UserManager.AddToRoleAsync(adminUser, adminRoleName)).CheckErrors();
                }

                if (!adminUser.IsInOrganizationUnit(organization.Id))
                {
                    await UserManager.AddToOrganizationUnitAsync(adminUser.Id, organization.Id);
                }
            }
        }
    }
}
